package com.bensonbasement.snorflux.server.world;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.List;

import com.bensonbasement.snorflux.memory.WorldElementInfo;
import com.bensonbasement.snorflux.server.ContentRepository;
import com.bensonbasement.snorflux.server.world.prototypes.WorldElementPrototype;

public abstract class WorldElement implements Serializable {
	
	// Attributes
	private String glyph = null;
	private String name = null;
	//
	
	private final long worldElementID;
	private final World world;
	
	// Whether or not the WorldElement has been "brought to life".
	private boolean animated = false;
	
	// TODO: Make prototypes transient.
	private transient WorldElementPrototype prototype;
	private final String prototypeID; // Used for de-serialization.
	
	public WorldElement(World world, WorldElementPrototype prototype) {
		this.world = world;
		this.prototype = prototype;
		this.prototypeID = prototype.getPrototypeID();
		this.worldElementID = world.registerWorldElement(this);
	}
	
	public World getWorld() {
		return this.world;
	}
	
	/**
	 * 
	 * @return This WorldElement and all its children.
	 */
	public abstract List<WorldElement> getAllWorldElements();

	public long getWorldElementID() {
		return worldElementID;
	}
	
	public abstract Level getLevel();
	
	public abstract WorldElementInfo render(IViewer viewer);

	public String getGlyph() {
		if (this.glyph != null) {
			return this.glyph;
		}
		else {
			return this.prototype.findGlyph();
		}
	}

	public String getName() {
		if (this.name != null) {
			return this.name;
		}
		else {
			return this.prototype.findName();
		}
	}

	protected WorldElementPrototype getPrototype() {
		return prototype;
	}
	
	protected void triggerInfoRefresh() {
		this.triggerInfoRefresh(null);
	}
	
	protected void triggerInfoRefresh(Level otherLevel) {
		if (this.animated) {
			Level currentLevel = this.getLevel();
			if (currentLevel != null) {
				currentLevel.forceInfoRefresh(this);
			}
			if (otherLevel != null && otherLevel != currentLevel) {
				otherLevel.forceInfoRefresh(this);
			}
		}
	}

	public void setName(String name) {
		this.name = name;
		// TODO: Add info refresh stuff.
	}
	
	protected boolean isAnimated() {
		return this.animated;
	}
	
	public final void animate() {
		this.animated = true;
		this.animateHelper();
	}
	
	protected void animateHelper() {
		this.triggerInfoRefresh();
	}
	
	/**
	 * Called when the object is deserialized.
	 */
	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
		in.defaultReadObject();
		prototype = ContentRepository.getInstance().getPrototype(prototypeID);
	}
}
